﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Xml.Linq;
using System.Net;
using System.Xml;
using System.Drawing;
using System.IO;

namespace LocationAwareness
{
    public delegate void DelegatePoisUpdated(IEnumerable<POI> pois);
    public delegate void DelegateDetailInfoGot(PoiDetail poiDetail);
    public delegate void DelegateDefaultImageGot(Bitmap bitmap, string oid);
    public delegate void DelegateDetailUpdated(PoiDetail poiDetail);

    public delegate void UTzel(string text);

    public class ServiceHandler
    {
        private GPSInterface gpsHandling;
        private GPSUrl gpsUrl;
        private PoiDetail poiDetail;

        //Filter sended on server site
        private string filter;

        //For session on server
        private string sessionID;

        //Max. distance to POI shown
        private string maxDistance;

        public event DelegatePoisUpdated PoisUpdated;
        public event DelegateDetailInfoGot DetailInfoGot;
        public event DelegateDefaultImageGot DefaultImageGot;
        public event DelegateDetailUpdated DetailUpdated;
        public event UTzel utzel;

        public ServiceHandler()
        {
            gpsHandling = (GPSInterface)new GPSHandling();
            gpsHandling.gpsDriverInitialized += new GpsDriverInitialized(gpsHandling_gpsDriverInitialized);
            gpsHandling.openGPS();

            filter = "";
            sessionID = "";
            maxDistance = "";
        }

        /// <summary>
        /// URL of the Server providing LocAwa
        /// </summary>
        public string ServiceUrl
        {
            set
            {
                gpsUrl = new GPSUrl(value);
            }

            get
            {
                return gpsUrl.ServiceUrl;
            }
        }

        /// <summary>
        /// Specifies the file on the webserver the application connects to for the detail view.
        /// The string has to contain the substrings "{0}", "{1}" and "{2}".
        /// "{0}" will be replaced by the actual value of gps latitude.
        /// "{1}" will be replaced by the actual value of gps longtitude.
        /// "{2}" will be replaced by the value of the ID from the object, wich will be shown. 
        /// </summary>
        public string DetailFile
        {
            set
            {
                gpsUrl.DetailFile = value;

            }

            get
            {
                return gpsUrl.DetailFile;
            }
        }

        /// <summary>
        /// Specifies the file on the webserver the application connects to for the detail view.
        /// The string has to contain the substrings "{0}", "{1}" and "{2}".
        /// "{0}" will be replaced by the actual value of gps latitude.
        /// "{1}" will be replaced by the actual value of gps longtitude.
        /// "{2}" will be replaced by the value of the ID from the object, wich will be shown. 
        /// </summary>
        public string UpdateDetailFile
        {
            set
            {
                gpsUrl.UpdateDetailFile = value;
            }

            get
            {
                return gpsUrl.UpdateDetailFile;
            }
        }

        /// <summary>
        /// Returns the longtitude value of the actual position
        /// </summary>
        public double Longtitude
        {
            get
            {
                return gpsHandling.getLongtitude();
            }
        }

        /// <summary>
        /// Returns the latitude value of the actual position
        /// </summary>
        public double Latitude
        {
            get
            {
                return gpsHandling.getLatitude();
            }
        }

        /// <summary>
        /// Specifies the file on the webserver the application connects to for the default view.
        /// The string has to contain the substrings "{0}" and "{1}".
        /// "{0}" will be replaced by the actual value of gps latitude.
        /// "{1}" will be replaced by the actual value of gps longtitude.
        /// </summary>
        public string DefaultFile
        {
            set
            {
                gpsUrl.DefaultFile = value;
            }

            get
            {
                return gpsUrl.DefaultFile;
            }
        }

        /// <summary>
        /// Method to set the filter for an application on server site.
        /// </summary>
        public void sendFilter(Dictionary<string, bool> filterDict)
        {
            String s = "";
            bool firstFil = true;
            foreach (KeyValuePair<string, bool> t in filterDict)
            {
                if (t.Value)
                {
                    if (firstFil)
                    {
                        s = s + t.Key;
                        firstFil = false;
                    }
                    else
                        s = s + "," + t.Key;
                }
            }

            if (s.Length == 0)
                filter = "&filter=cinema,misc,supermarket,rest,bar,club,traffic,fastfood,culture";
            else
                filter = "&filter=" + s;
        }

        /// <summary>
        /// Event method, occurs when the GPS driver is intialized and starts first update of the application.
        /// </summary>
        private void gpsHandling_gpsDriverInitialized()
        {
            GpsPosition position = gpsHandling.getGpsPosition();

            if (gpsUrl != null && (position.Latitude != 0.0 || position.Longitude != 0.0))
            {
                gpsUrl.Position = position;
                gpsHandling.gpsDriverInitialized -= gpsHandling_gpsDriverInitialized;

                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(gpsUrl.getGpsUrlDefault().ToString() + maxDistance);
                request.Method = "GET";
                request.BeginGetResponse(new AsyncCallback(onResponseDefault), request);
            }
        }

        /// <summary>
        /// Dates up the POIs in the near are of the user.
        /// </summary>
        public void updatePois()
        {
            gpsUrl.Position = gpsHandling.getGpsPosition();

            utzel(gpsHandling.getLatitude() + "" + gpsHandling.getLongtitude());

            //XmlDocument doc = new XmlDocument();
            //string s = gpsUrl.getGpsUrl().ToString();
            //doc.Load(s);
            //s = doc.ToString();

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(gpsUrl.getGpsUrlDefault().ToString() + maxDistance + filter + sessionID);
            
            filter = "";  //Filter wieder auf leeren String setzen wenn noch mit session realisiert
            //if(sessionID.Length != 0)
            maxDistance = "";

            request.Method = "GET";
            request.BeginGetResponse(new AsyncCallback(onResponseDefault), request);
        }

        /// <summary>
        /// AsyncCallback Method, occurs when data for default view arrives from server.
        /// </summary>
        /// <param name="asyncResult">Result containing the response stream</param>
        private void onResponseDefault(IAsyncResult asyncResult)
        {
            try
            {
                HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
                HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult);

                //Stream stream = response.GetResponseStream();
                //StreamReader reader = new StreamReader(stream);
                //string responseFromServer = reader.ReadToEnd();
                //XElement locawa = XElement.Load(XmlReader.Create(stream));

                //utzel("durch gelaufen");

                XElement locawa = XElement.Load(XmlReader.Create(response.GetResponseStream()));
                response.Close();

                //Beim Entfernen der Testzeilen, auch using System.IO; entfernen
                //Stream stream = response.GetResponseStream();
                //StreamReader reader = new StreamReader(stream);
                //string responseFromServer = reader.ReadToEnd();
                //Testen mit lokaler Xml, using System.IO; entfernen
                //Stream ms = new MemoryStream(ASCIIEncoding.ASCII.GetBytes(global::LocationAwareness.TestRess.TestPoi));
                //XElement locawa = XElement.Load(XmlReader.Create(ms));


                sessionID = "&" + (string)locawa.Attribute("SID");


                IEnumerable<POI> pois =
                from p in locawa.Elements("poi")
                orderby (string)p.Attribute("DIS") descending
                select new POI
                {
                    Name = (string)p.Attribute("NAME"),
                    Distance = (string)p.Attribute("DIS"),
                    Categorie = (string)p.Attribute("CAT"),
                    ObjectID = (string)p.Attribute("OID"),
                    ImagePath = gpsUrl.ServiceUrl + "/" + (string)p.Attribute("ICON")
                };

                PoisUpdated(pois);
            }
            catch (WebException)
            {
                
            }
        }

        /// <summary>
        /// Requesting for the image of a POI displayed in default view.
        /// </summary>
        /// <param name="path">Path on server of requesting image</param>
        public void getDefaultImage(string path)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(path);
            request.Method = "GET";
            request.BeginGetResponse(new AsyncCallback(onResponseDefaultImage), request);
        }

        /// <summary>
        /// AsyncCallback Method, occurs when the image for default view arrives from server.
        /// </summary>
        /// <param name="asyncResult">Result containing the response stream</param>
        private void onResponseDefaultImage(IAsyncResult asyncResult)
        {
            try
            {
                HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
                HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult);
                Bitmap b = new Bitmap(response.GetResponseStream());
                response.Close();

                DefaultImageGot(b, request.Address.ToString());
            }
            catch (WebException)
            {
                
                throw;
            }
        }

        /// <summary>
        /// Requests the detail information of the passed PoiDetail instance.
        /// </summary>
        /// <param name="p">PoiDetail instance detail informations are requested</param>
        public void getDetailInfo(PoiDetail p)
        {
            poiDetail = p;
            gpsUrl.Position = gpsHandling.getGpsPosition();
            gpsUrl.ObjectID = poiDetail.ObjectID;

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(gpsUrl.getGpsUrlDetail());
            request.Method = "GET";
            request.BeginGetResponse(new AsyncCallback(onResponseDetail), request);
        }

        /// <summary>
        /// AsyncCallback Method, occurs when data for default view arrives from server.
        /// </summary>
        /// <param name="asyncResult">Result containing the response stream</param>
        private void onResponseDetail(IAsyncResult asyncResult)
        {
            try
            {
                HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
                HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult);

                //StreamReader reader = new StreamReader(response.GetResponseStream());
                //// Read the content.
                //string responseFromServer = reader.ReadToEnd();

                XElement locawa = XElement.Load(XmlReader.Create(response.GetResponseStream()));
                response.Close();

                IEnumerable<PoiDetail> poidetails =
                from p in locawa.Elements("poidetail")
                orderby (string)p.Attribute("DIS")
                select new PoiDetail
                {
                    Address = (string)p.Attribute("ADDRESS"),
                    StreetNr = (string)p.Attribute("STNR"),
                    Distance = (double)p.Attribute("DIS"),
                    Desc = (string)p.Attribute("DESC"),
                    Opening = (string)p.Attribute("OPENING"),
                    ImagePath = (string)p.Attribute("IMAGE"),
                    MapPath = gpsUrl.ServiceUrl + "/" + (string)p.Attribute("MAP"),
                    Lat = (double)p.Attribute("POILAT"),
                    Long = (double)p.Attribute("POILON")
                };

                foreach (PoiDetail p in poidetails)
                {
                    poiDetail.copyAttributes(p);
                }

                //HttpWebRequest requestImage = (HttpWebRequest)WebRequest.Create("http://gws.maps.yahoo.com/mapimage?MAPDATA=8Z8wped6wXWCiOAsIjnwUKLbMhPVbCoQ6JDLHQzxjKI43Ol1yV_R91BqMW2iaH_DsVmFvJBHRzPdbTeXZQ7QTLJ_LCXhW_WfjZk94iEDERqpTYCHw8FE0UfTG3lvwBGPUggmzSx0jMjpq.gPw8tkyIY-&mvt=m&cltype=onnetwork&.intl=us&appid=YD-4g6HBf0_JX0yq2IsdnV1Ne9JTpKxQ3Miew--&oper=&_proxy=ydn,xml");
                HttpWebRequest requestImage = (HttpWebRequest)WebRequest.Create(gpsUrl.ServiceUrl + "/" + poiDetail.ImagePath);
                requestImage.Method = "GET";
                requestImage.BeginGetResponse(new AsyncCallback(onResponseDetailImage), requestImage);
            }
            catch (WebException)
            {
                
            }
        }

        /// <summary>
        /// AsyncCallback Method, occurs when data for default view arrives from server.
        /// </summary>
        /// <param name="asyncResult">Result containing the response stream</param>
        private void onResponseDetailImage(IAsyncResult asyncResult)
        {
            try
            {
                HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
                HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult);
                poiDetail.DetailImage = new Bitmap(response.GetResponseStream());
                response.Close();

                DetailInfoGot(poiDetail);
            }
            catch (WebException)
            {
                
            }
        }

        /// <summary>
        /// Method to update displayed PoiDetailPanel.
        /// </summary>
        public void updateDetail()
        {
            gpsUrl.Position = gpsHandling.getGpsPosition();
            gpsUrl.ObjectID = poiDetail.ObjectID;

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(gpsUrl.getGpsUrlUpdateDetail());
            request.Method = "GET";
            request.BeginGetResponse(new AsyncCallback(onResponseUpdateDetail), request);
        }

        /// <summary>
        /// AsyncCallback Method, occurs when data to update detail view arrives from server.
        /// </summary>
        /// <param name="asyncResult">Result containing the response stream</param>
        private void onResponseUpdateDetail(IAsyncResult asyncResult)
        {
            try
            {
                HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
                HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult);

                //StreamReader reader = new StreamReader(response.GetResponseStream());
                //// Read the content.
                //string responseFromServer = reader.ReadToEnd();

                XElement locawa = XElement.Load(XmlReader.Create(response.GetResponseStream()));
                response.Close();

                IEnumerable<PoiDetail> poidetails =
                from p in locawa.Elements("poidetail")
                orderby (string)p.Attribute("DIS")
                select new PoiDetail
                {
                    Distance = (double)p.Attribute("DIS"),
                };

                foreach (PoiDetail p in poidetails)
                {
                    poiDetail.updatePoiDetail(p.Distance);
                }

                DetailUpdated(poiDetail);
            }
            catch (WebException)
            {
                
            }
        }

        /// <summary>
        /// Method to set the max distance of shown pois on the client application.
        /// </summary>
        public void SetMaxDistance(string p)
        {
            maxDistance = "&maxDist=" + p;
        }

        ~ServiceHandler()
        {
            gpsHandling.closeGPS();
        }
    }
}
